/**
 * Author:yuiop
 * Date:2022/7/25 10:18
 */
package com.devops.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.auth.sts.AssumeRoleRequest;
import com.aliyuncs.auth.sts.AssumeRoleResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.devops.admin.model.DOSClusterInfo;
import com.devops.admin.po.DOSReleasePo;
import com.devops.admin.redis.RedisService;
import com.devops.admin.service.DOSAliyunLogService;
import com.devops.admin.service.DOSClusterInfoService;
import com.devops.admin.service.DOSReleaseService;
import com.devops.admin.util.Assert;
import com.devops.constant.DOSAliyunRoleEnum;
import com.devops.constant.DevOpsConstant;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;

@Service
public class DOSAliyunLogServiceImpl implements DOSAliyunLogService {

    @Autowired
    private DOSReleaseService dosReleaseService;
    @Autowired
    private DOSClusterInfoService dosClusterInfoService;
    @Autowired
    private RedisService redisService;

    private final String ACK_KEY = "DOS_LOG_CREDENTIALS";
    private final String ACCESS_KEY_ID = "access-key-id";
    private final String ACCESS_KEY_SECRET = "access-key-secret";
    private final String SECURITY_TOKEN = "security-Token";

    @Resource
    IAcsClient logAcsClient;

    @Override
    public String getLogFramePageUrl(String projectName, String logStoreName) {

        //发起请求，并得到响应。
        String loginUrl = "";
        try {
            String signInToken;
            // 先从缓存里面取，没有数据或者时间失效了，再调用获取ack的方法
            Map<Object, Object> ackMap = redisService.hGetAll(ACK_KEY);
            int count = 2;
            // 如果token为空重试获取token，最多尝试两次
            do {
                if (CollectionUtil.isEmpty(ackMap)) {
                    // 获取token
                    signInToken = getSignInToken();
                } else {
                    signInToken = getSignInToken(ackMap.get(ACCESS_KEY_ID).toString(),
                            ackMap.get(ACCESS_KEY_SECRET).toString(), ackMap.get(SECURITY_TOKEN).toString());
                }
                ackMap.clear();
            }while (StrUtil.isEmpty(signInToken) && --count > 0);

            Assert.isTrue(StrUtil.isNotBlank(signInToken),"获取signInToken出错");

            String pageUrl = DevOpsConstant.AliyunLogUrl.LOG_SEARCH_ALL
                    .replace("<Project名称>", projectName)
                    // logStoreName:release的名称
                    .replace("<日志库名称>", logStoreName);
            loginUrl = getHdmLoginUrl(pageUrl, signInToken);
        } catch (URISyntaxException | ClientException | IOException e) {
            e.printStackTrace();
        }
        return loginUrl;

    }

    @Override
    public String getLoginUrl(Integer releaseId) {
        // 根据releaseId获取release的详细信息
        DOSReleasePo releasePo = dosReleaseService.getReleaseDetailById(releaseId);
        // 判断查询结果是否为空
        if (ObjectUtil.isEmpty(releasePo)) {
            return null;
        }
        // 获取集群info信息，获取集群项目名称
        DOSClusterInfo clusterInfo = dosClusterInfoService.getOne(Wrappers.<DOSClusterInfo>lambdaQuery()
                .eq(DOSClusterInfo::getClusterId, releasePo.getClusterId()));

        Assert.isTrue(StrUtil.isNotBlank(clusterInfo.getClusterLogProjectName()),"集群项目名称为空");

        return this.getLogFramePageUrl(clusterInfo.getClusterLogProjectName(), releasePo.getReleaseName());
    }

    @Override
    public String redirect(Integer releaseId) {
        // 根据releaseId获取release的详细信息
        DOSReleasePo releasePo = dosReleaseService.getReleaseDetailById(releaseId);
        // 判断查询结果是否为空
        if (ObjectUtil.isEmpty(releasePo)) {
            return null;
        }
        // 获取集群info信息，获取集群项目名称
        DOSClusterInfo clusterInfo = dosClusterInfoService.getOne(Wrappers.<DOSClusterInfo>lambdaQuery()
                .eq(DOSClusterInfo::getClusterId, releasePo.getClusterId()));

        return getLogUrl(clusterInfo.getClusterLogProjectName(), releasePo.getReleaseName());
    }

    /**
     * @Author heyabo
     * @Description 设置缓存数据
     * @Date 10:28 2022/7/27
     * @Param [response]
     * @return void
     **/
    private void setCache(AssumeRoleResponse response) {
        Map<String, Object> map = new HashMap<>(16);
        // 设置缓存标量
        map.put(ACCESS_KEY_ID, response.getCredentials().getAccessKeyId());
        map.put(ACCESS_KEY_SECRET, response.getCredentials().getAccessKeySecret());
        map.put(SECURITY_TOKEN, response.getCredentials().getSecurityToken());
        // 缓存数据，设置30分钟过去
        redisService.hSetAll(ACK_KEY, map, 1800L);
    }

    /**
     * @return com.aliyuncs.auth.sts.AssumeRoleResponse
     * @Author heyabo
     * @Description 获取临时的ack参数，有效期1小时
     * @Date 08:57 2022/7/27
     * @Param []
     **/
    private AssumeRoleResponse getResponse() throws ClientException {
        //构造请求，设置参数。关于参数含义和设置方法，请参见《API参考》。
        AssumeRoleRequest request = new AssumeRoleRequest();
        request.setRoleArn(DOSAliyunRoleEnum.LOG_ROlE.getRoleArn());
        request.setRoleSessionName(DOSAliyunRoleEnum.LOG_ROlE.getRoleSessionName());
        request.setSysMethod(MethodType.POST);
        AssumeRoleResponse response = logAcsClient.getAcsResponse(request);
        return response;
    }

    /**
     * @Author heyabo
     * @Description 重新调用获取日志url地址的方法，这里不直接从缓存中取数据
     * @Date 10:36 2022/7/27
     * @Param [projectName, logStoreName]
     * @return java.lang.String
     **/
    private String getLogUrl(String projectName, String logStoreName) {
        //发起请求，并得到响应。
        String loginUrl = "";
        try {
            // 获取ack
            AssumeRoleResponse response = getResponse();
            // 缓存获取的ack
            setCache(response);
            // 获取临时token
            String signInToken = getSignInToken(response.getCredentials().getAccessKeyId(),
                    response.getCredentials().getAccessKeySecret(), response.getCredentials().getSecurityToken());

            String pageUrl = DevOpsConstant.AliyunLogUrl.LOG_SEARCH_ALL
                    .replace("<Project名称>", projectName)
                    // logStoreName:release的名称
                    .replace("<日志库名称>", logStoreName);
            loginUrl = getHdmLoginUrl(pageUrl, signInToken);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return loginUrl;
    }

    private String getSignInToken(String accesskeyId, String accessKeySecret, String securityToken)
            throws IOException, URISyntaxException {

        URIBuilder builder = new URIBuilder(DevOpsConstant.AliyunLogUrl.SIGN_IN_DOMAIN);
        builder.setParameter("Action", "GetSigninToken")
                .setParameter("AccessKeyId", accesskeyId)
                .setParameter("AccessKeySecret", accessKeySecret)
                .setParameter("SecurityToken", securityToken)
                .setParameter("TicketType", "mini");
        HttpGet request = new HttpGet(builder.build());
        CloseableHttpClient httpclient = HttpClients.createDefault();
        try (CloseableHttpResponse response = httpclient.execute(request)) {
            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
                String context = EntityUtils.toString(response.getEntity());
                JSONObject jsonObject = JSON.parseObject(context);
                return jsonObject.getString("SigninToken");
            } else {
                System.out.println(response.getStatusLine());
            }
        }
        return null;
    }

    private String getHdmLoginUrl(String pageUrl, String signInToken) throws URISyntaxException {
        URIBuilder builder = new URIBuilder(DevOpsConstant.AliyunLogUrl.SIGN_IN_DOMAIN);
        builder.setParameter("Action", "Login");
        // 登录失效跳转的地址，一般配置为自建WEB配置302跳转的URL
        builder.setParameter("LoginUrl", DevOpsConstant.AliyunLogUrl.REDIRECT_URL);
        // 实际访问 DAS 的页面，比如全局大盘，实时大盘，某个实例详情等
        builder.setParameter("Destination", pageUrl);
        builder.setParameter("SigninToken", signInToken);
        HttpGet request = new HttpGet(builder.build());
        String uri = request.getURI().toString();
        if (StrUtil.isEmpty(signInToken) || !uri.contains(signInToken)){
            uri = DevOpsConstant.AliyunLogUrl.REDIRECT_URL;
        }

        return uri;
    }

    /**
     * @Author heyabo
     * @Description 获取token
     * @Date 09:28 2022/7/28
     * @Param []
     * @return java.lang.String
     **/
    private String getSignInToken() throws ClientException, IOException, URISyntaxException {
        String signInToken = null;
        // 获取ack
        AssumeRoleResponse response = getResponse();
        // 缓存获取的ack
        setCache(response);
        // 获取临时token
        signInToken = getSignInToken(response.getCredentials().getAccessKeyId(),
                response.getCredentials().getAccessKeySecret(), response.getCredentials().getSecurityToken());
        return signInToken;
    }
}
